using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;

namespace WindowsAPI
{
    public static partial class Kernel32
    {
        internal static class DeviceFunctionsTest
        {
            //public static bool DeviceIoControl(
            //    IntPtr hDevice,
            //    uint dwIoControlCode,
            //    ref long buffer,
            //    int bufferSize,
            //    ref NativeOverlapped pOverlapped)
            //{
            //    var NoReturn = 0;
            //    return Kernel32.DeviceIoControl(
            //        hDevice,
            //        dwIoControlCode,
            //        ref buffer,
            //        bufferSize,
            //        ref buffer,
            //        bufferSize,
            //        ref NoReturn,
            //        ref pOverlapped);
            //}

            //[Link("http://pinvoke.net/default.aspx/kernel32/DeviceIoControl.html")]
            //public static bool PlxIntrAttach(IntPtr handle, PlxIntr intrType)
            //{
            //    // Call to PLX card to attach wait event to an Interrupts
            //    // Declare control code
            //    DriverMsgs AttachMsg = DriverMsgs.MsgIntrAttach;
            //    uint Control = CtrCode(cFileDeviceUnknown, (int)AttachMsg, cMethodBuffered, cFileAnyAccess);
            //    // Set return variable up
            //    var Status = false;
            //    // Initialize the "buffer"
            //    long DeviceBuffer = 0;

            //    // Fill the buffer with the interrupt bitflag
            //    DeviceBuffer = (long)intrType;
            //    // Call the P/Invoked function through masking method
            //    Status = DeviceIoControl(handle, Control, ref DeviceBuffer, 8, ref AdcOverlapData);
            //    // Return with Status
            //    return Status;
            //}

            public static byte[] ReadRawPartition(int harddisk, int partition, int sectors)
            {
                var name = string.Format(@"\\?\GLOBALROOT\Device\Harddisk{0}\Partition{1}",
                  harddisk, partition);
                var handle = CreateFile(name, FileAccess.Read, FileShare.ReadWrite,
                    IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);

                int dummy;
                var diskGeo = new DISK_GEOMETRY();
                DeviceIoControl(handle, eIOControlCode.DiskGetDriveGeometry, IntPtr.Zero, 0, out diskGeo,
                    Marshal.SizeOf(diskGeo), out dummy, IntPtr.Zero);
                var partInfo = new PARTITION_INFORMATION();
                DeviceIoControl(handle, eIOControlCode.DiskGetPartitionInfo,
                  IntPtr.Zero, 0, out partInfo, Marshal.SizeOf(partInfo), out dummy, IntPtr.Zero);

                var length = sectors * diskGeo.BytesPerSector;
                var data = new byte[length];
                uint len;
                if(!ReadFile(handle, data, (uint)length, out len, IntPtr.Zero))
                    return null;
                if(length != len) Array.Resize(ref data, (int)len);
                return data;
            }
        }

        [DllImport(FileName, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(
                    SafeFileHandle HDevice,
                    eIOControlCode DiskGetDriveGeometry,
                    IntPtr LpInBuffer,
                    int NInBufferSize,
                    out DISK_GEOMETRY LpOutBuffer,
                    int NOutBufferSize,
                    out int LpBytesReturned,
                    IntPtr LpOverlapped);

        [DllImport(FileName, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(
                    SafeFileHandle HDevice,
                    eIOControlCode DiskGetPartitionInfo,
                    IntPtr LpInBuffer,
                    int NInBufferSize,
                    out PARTITION_INFORMATION LpOutBuffer,
                    int NOutBufferSize,
                    out int LpBytesReturned,
                    IntPtr LpOverlapped);


        [DllImport(FileName, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(
            IntPtr hDevice,
            int dwIoControlCode,
            IntPtr lpInBuffer,
            int nInBufferSize,
            IntPtr lpOutBuffer,
            int nOutBufferSize,
            out int lpBytesReturned,
            IntPtr lpOverlapped);

        [DllImport(FileName, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DeviceIoControl(
                IntPtr hDevice,
                uint dwIoControlCode,
                ref long InBuffer,
                int nInBufferSize,
                ref long OutBuffer,
                int nOutBufferSize,
                ref int pBytesReturned,
                [In] ref NativeOverlapped lpOverlapped);

        [DllImport(FileName, SetLastError = false, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(
                IntPtr hDevice,
                eIOControlCode IoControlCode,
                [MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer,
                uint nInBufferSize,
                [MarshalAs(UnmanagedType.AsAny)] [Out] object OutBuffer,
                uint nOutBufferSize,
                ref uint pBytesReturned,
                [In] ref NativeOverlapped Overlapped);

        [DllImport(FileName, SetLastError = false, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(
                IntPtr hDevice,
                eIOControlCode dwIoControlCode,
                IntPtr InBuffer,
                int nInBufferSize,
                IntPtr OutBuffer,
                int nOutBufferSize,
                ref int pBytesReturned,
                [In] ref NativeOverlapped lpOverlapped
        );

        /// <summary>
        /// The GetDriveType function determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive
        /// </summary>
        /// <param name="lpRootPathName">
        /// A pointer to a null-terminated string that specifies the root directory 
        /// and returns information about the disk.A trailing backslash is required. 
        /// If this parameter is NULL, the function uses the root of the current directory.
        /// </param>
        [DllImport(FileName, SetLastError = false)]
        public static extern DriveType GetDriveType([MarshalAs(UnmanagedType.LPStr)] string lpRootPathName);

        [DllImport(FileName, SetLastError = false)]
        public static extern long GetVolumeInformation(
            string PathName,
            StringBuilder VolumeNameBuffer,
            uint VolumeNameSize,
            ref uint VolumeSerialNumber,
            ref uint MaximumComponentLength,
            ref uint FileSystemFlags,
            StringBuilder FileSystemNameBuffer,
            uint FileSystemNameSize);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DISK_GEOMETRY
    {
        public long Cylinders;
        public MEDIA_TYPE MediaType;
        public int TracksPerCylinder;
        public int SectorsPerTrack;
        public int BytesPerSector;

        public long DiskSize { get { return Cylinders * TracksPerCylinder * SectorsPerTrack * BytesPerSector; } }
    }

    public enum MEDIA_TYPE : uint
    {
        Unknown,
        F5_1Pt2_512,
        F3_1Pt44_512,
        F3_2Pt88_512,
        F3_20Pt8_512,
        F3_720_512,
        F5_360_512,
        F5_320_512,
        F5_320_1024,
        F5_180_512,
        F5_160_512,
        RemovableMedia,
        FixedMedia,
        F3_120M_512,
        F3_640_512,
        F5_640_512,
        F5_720_512,
        F3_1Pt2_512,
        F3_1Pt23_1024,
        F5_1Pt23_1024,
        F3_128Mb_512,
        F3_230Mb_512,
        F8_256_128,
        F3_200Mb_512,
        F3_240M_512,
        F3_32M_512
    }

    //    [StructLayout(LayoutKind.Sequential)]
    //    public struct DRIVE_LAYOUT_INFORMATION_EX
    //    {
    //        public PARTITION_STYLE PartitionStyle;
    //
    //        public int PartitionCount;
    //
    //        public DRIVE_LAYOUT_INFORMATION_UNION DriveLayoutInformaiton;
    //
    //        [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 0x16)]
    //        public PARTITION_INFORMATION_EX[] PartitionEntry;
    //    }

    [StructLayout(LayoutKind.Explicit)]
    public struct DRIVE_LAYOUT_INFORMATION_UNION
    {
        [FieldOffset(0)]
        public DRIVE_LAYOUT_INFORMATION_MBR Mbr;

        [FieldOffset(0)]
        public DRIVE_LAYOUT_INFORMATION_GPT Gpt;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DRIVE_LAYOUT_INFORMATION_MBR { public int Signature; }

    [StructLayout(LayoutKind.Sequential)]
    public struct DRIVE_LAYOUT_INFORMATION_GPT
    {
        public Guid DiskId;
        public long StartingUsableOffset;
        public long UsableLength;
        public int MaxPartitionCount;
    }

    public enum PARTITION_STYLE
    {
        MasterBootRecord = 0,
        GuidPartitionTable = 1,
        Raw = 2
    }

    //    [StructLayout(LayoutKind.Sequential)]
    //    public struct PARTITION_INFORMATION_EX
    //    {
    //        public PARTITION_STYLE PartitionStyle;
    //
    //        public long StartingOffset;
    //
    //        public long PartitionLength;
    //
    //        public int PartitionNumber;
    //
    //        public bool RewritePartition;
    //
    //        public PARTITION_INFORMATION_UNION DriveLayoutInformaiton;
    //    }

    //    [StructLayout(LayoutKind.Explicit)]
    //    public struct PARTITION_INFORMATION_UNION
    //    {
    //        [FieldOffset(0)]
    //        public PARTITION_INFORMATION_MBR Mbr;
    //        [FieldOffset(0)]
    //        public PARTITION_INFORMATION_GPT Gpt;
    //    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PARTITION_INFORMATION_GPT
    {
        public Guid PartitionType;
        public Guid PartitionId;
        public ulong Attributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 36)]
        public string Name; //[36];
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PARTITION_INFORMATION_MBR
    {
        public byte PartitionType;
        public bool BootIndicator;
        public bool RecognizedPartition;
        public uint HiddenSectors;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PARTITION_INFORMATION
    {
        public long StartingOffset;
        public long PartitionLength;
        public int HiddenSectors;
        public int PartitionNumber;
        public byte PartitionType;
        [MarshalAs(UnmanagedType.I1)]
        public bool BootIndicator;
        [MarshalAs(UnmanagedType.I1)]
        public bool RecognizedPartition;
        [MarshalAs(UnmanagedType.I1)]
        public bool RewritePartition;
    }
}